<template>
  <div class="cost-item">
    <div class="cost-item-body" ref="reference">
      <cg-gird shape="rectangle" :column="{ gutter: 4, colNum: 6 }">
        <cg-gird-item v-for="item in list" :role-item="item.id" :key="`${item.id}`">
          <cg-image :path="item.head_path"></cg-image>
          <template v-if="lastPopperId === item.id">
            <cg-button class="cost-item-close" icon="close" @click="deleteRow(item)" color="#666" />
            <div class="cost-item-num">{{ item.num }}</div>
          </template>
        </cg-gird-item>
        <cg-gird-item>
          <cg-button class="cost-item-btn" icon="plus" @click="openRow()" color="#666" />
        </cg-gird-item>
      </cg-gird>
    </div>

    <div class="cost-item-detail-panel" ref="popper" v-show="showPopper" :style="panelStyle">
      <template v-if="popperData">
        <div class="cost-item-title">
          <div class="cost-item-number">{{ popperData.name }}</div>
          <div class="cost-item-number">{{ popperData.num }}</div>
        </div>
        <div class="cost-item-content">{{ popperData.description }}</div>
      </template>
    </div>
    <cg-dialog class="cost-item-dialog" v-model:visible="visible" title="选择列表">
      <div class="cost-item-dialog-content">
        <div class="left-col">
          <cg-image :path="dialogSelectedItem.head_path"></cg-image>
        </div>
        <div class="right-col">
          <div class="cost-item-select">
            <cg-select v-model="dialogSelected" :options="options"></cg-select>
          </div>
          <div class="cost-item-description">
            {{ dialogSelectedItem.description }}
          </div>
        </div>
      </div>
      <template v-slot:footer>
        <div class="left-col">
          <label>数量：</label>
          <cg-input-number v-model="dialogNum"></cg-input-number>
        </div>
        <div class="right-col">
          <cg-button @click="dialogConfirm">确定</cg-button>
        </div>
      </template>
    </cg-dialog>
  </div>
</template>

<script setup lang="ts">
import {  ref, computed, watch, onMounted, onUnmounted, ref as vueRef } from 'vue'
import {
  switchValue,
  isInitString,
  getRelativeOffset,
  objectFrom,
  isFormalEqual,
} from '../../../../tools/index'
import { isObject } from '../../../../tools/types'
import { on, off, isInRect } from '../../../../tools/dom'
import _ from 'lodash'

defineOptions({
  name: 'cost-item'
})

const props = defineProps<{
  modelValue: Array<{
    id: string | number
    num: number
  }>
  disabled: boolean
  options: Array<{
    id: string | number
    name: string
    description: string
    head_path: string
  }>
}>({
  modelValue: { type: Array, required: true },
  disabled: { type: Boolean, default: false },
  options: { type: Array, required: true }
})

const emit = defineEmits<{
  (e: 'update:modelValue', value: Array<{
    id: string | number
    num: number
  }>): void
}>()

// Data
const visible = ref(false)
const selected = ref<Array<{
  id: string | number
  num: number
\}>>([])
const dialogSelected = ref<number | undefined>(undefined)
const dialogNum = ref(0)
const showPopper = ref(false)
const lastPopperId = ref<number | string | undefined>(undefined)
const popperData = ref<{
  id: string | number
  name: string
  num: number
  description: string
  head_path: string
} | undefined>(undefined)
const panelLeft = ref(0)
const panelTop = ref(0)

// Refs
const reference = vueRef<HTMLElement | null>(null)
const popper = vueRef<HTMLElement | null>(null)
let handleMove: ((e: MouseEvent) => void) | null = null

// Computed
const list = computed(() => {
  if (Array.isArray(props.options)) {
    return _.compact(
      selected.value.map((item) => {
        let detail = props.options.find((option) =>
          isFormalEqual(item.id, option.id)
        )
        return objectFrom(detail, item)
      })
    )
  }
  return []
})

const dialogSelectedItem = computed(() => {
  return switchValue(dialogSelected.value, props.options, 'id', _.toString)
})

const panelStyle = computed(() => {
  return {
    left: panelLeft.value + 'px',
    top: panelTop.value + 'px'
  }
})

// Methods
function updateValue() {
  selected.value = Array.isArray(props.modelValue) ? props.modelValue : []
}

function openRow(item?: {
  id: string | number
}) {
  if (item) {
    dialogSelected.value = _.toNumber(item.id)
  } else {
    dialogSelected.value = undefined
  }
  visible.value = true
}

function deleteRow(item: {
  id: string | number
}) {
  if (typeof item === 'object' && isInitString(item.id)) {
    let save = Array.isArray(selected.value) ? selected.value : []
    _.remove(save, (v) => isFormalEqual(v, item.id))
    selected.value = save
    emit('update:modelValue', selected.value)
  }
}

function dialogConfirm() {
  if (isObject(dialogSelectedItem.value)) {
    let save = Array.isArray(selected.value) ? selected.value : []
    const { id } = dialogSelectedItem.value
    _.remove(save, (v) => isFormalEqual(v, id))
    save.push({ id, num: dialogNum.value })
    selected.value = save
    visible.value = false
    dialogSelected.value = undefined
    emit('update:modelValue', selected.value)
  }
}

function bindEvents() {
  if (!reference.value) return

  const isRelation = (e: MouseEvent) => {
    if (e instanceof MouseEvent) {
      const { clientX, clientY } = e
      if (reference.value instanceof Element) {
        let items = reference.value.querySelectorAll('[role-item]')
        for (const item of items) {
          if (isInRect([clientX, clientY], item)) {
            return item
          }
        }
      }
      if (popper.value instanceof Element) {
        if (isInRect([clientX, clientY], popper.value)) {
          return true
        }
      }
    }
    return null
  }

  handleMove = _.throttle((e: MouseEvent) => {
    if (!reference.value) return

    let target = isRelation(e)
    if (target) {
      if (!showPopper.value) {
        lastPopperId.value = undefined
      }
      showPopper.value = true
      if (target instanceof Element) {
        const value = target.getAttribute('role-item')
        if (lastPopperId.value !== value) {
          const { clientX, clientY } = e
          const offset = getRelativeOffset(reference.value)
          panelLeft.value = clientX - offset.left
          panelTop.value = clientY - offset.top
          const data = list.value.find((item) =>
            isFormalEqual(item.id, value)
          )
          popperData.value = data
          lastPopperId.value = value
        }
      }
    } else {
      showPopper.value = false
    }
  }, 100)

  on(reference.value, 'mouseenter', handleMove)
  on(document, 'mousemove', handleMove)
}

function unBindEvents() {
  if (!reference.value || !handleMove) return

  off(reference.value, 'mouseenter', handleMove)
  off(document, 'mousemove', handleMove)
  handleMove = null
}

// Watch
watch(
  () => props.modelValue,
  () => {
    updateValue()
  }
)

// Lifecycle
onMounted(() => {
  updateValue()
  bindEvents()
})

onUnmounted(() => {
  unBindEvents()
})
</script>

<style lang="scss">
@use './style.scss';
</style>
